_G.require("preload");
_G["ffi"] = _G.require("ffi");
table.isHashMap = function (tab) {
    return (tab as any[]).length == 0;
}
table.isArray = function (tab) {
    return !table.isHashMap(tab);
}
table.tostring = (tab: any, symbol1 = " = ", symbol2 = ","): string => {
    if (typeof (tab) == "object") {
        return table.getElement(tab, symbol1, symbol2);
    }
}
table.getElement = function (tab: any, symbol1 = " = ", symbol2 = ",") {
    let ele = [];
    for (const key in tab) {
        let element = _G.rawget(tab, key);
        if (element) {
            if (typeof (element) == "object") {
                ele.push(`${key} ${symbol1} ${table.getElement(element)}`);
            } else {
                ele.push(`${key} ${symbol1} ${element}`);
            }
        }
    }

    return `{ ${ele.join(` ${symbol2} `)} }`
}
table.toJson = function (tab: any[]) {
    return table.tostring(tab, ":")
}
table.gc = function (target: any, f: Function) {
    let p = ffi.malloc(1);
    _G.rawset(target, "__gc", p);
    ffi.gc(p, f);
}





class io {

}

/**
 * io库
 */
namespace io {
    let _io = _G.rawget(_G, "io");
    /**
     * 打印数据
     * @param arg 可打印多个 
     */
    export function print(...arg: vararg) {
        _G.print(...arg);
    }
}

/**延时 */
function sleep(ms: number) {
    return ffi.C.Sleep(ms);
}

ffi.cdef(
    `
        void Sleep(int ms);
        int GetTickCount();
        void* malloc(size_t size);
        void* free(void* memblock);
        const char* GetCommandLineA();
        typedef const wchar_t* utf8str;
        typedef const char* str;
        int MultiByteToWideChar(
            int CodePage,
            int dwFlags,
            str lpMultiByteStr,
            int cchMultiByte,
            utf8str lpWideCharStr,
            int cchWideChar
        );
        
    `
);

/**
 * ffi扩展裤
 */
namespace ffi {
    let msvcrt = ffi.load("msvcrt")
    /**申请内存 */
    export function malloc(size: number) {
        return msvcrt.malloc(size);
    }
    /**释放内存 */
    export function free(cdata: any) {
        return msvcrt.free(cdata);
    }
    /**获取指针 */
    export function getPtr(p: string | number | boolean) {
        return ffi.cast("intptr_t", p);
    }
    /**指针到整数 */
    export function ptr2int(ptr: any) {
        return ffi.cast("int", ptr);
    }
    export function str2utfstr(ptr: any) {
        return ffi.C.MultiByteToWideChar()
    }

    /**申请字符串缓存 */
    export function buff(len: number) {
        return ffi.new("char[?]", len);
    }
};

/**
 * string 库
 */
namespace string {
    let _string = _G.string;
    export function load() {

    }
    export function save() {

    }
    export let join = _string.join as (this: void, tab: any, str: string) => any;
    /**返回一个类似printf的格式化字符串
     * 详细用法可以参考lua手册[lua手册链接](https://www.runoob.com/lua/lua-strings.html)
     * ### 格式字符串可能包含以下的转义码:
> - %c - 接受一个数字, 并将其转化为ASCII码表中对应的字符
- %d, %i - 接受一个数字并将其转化为有符号的整数格式
- %o - 接受一个数字并将其转化为八进制数格式
- %u - 接受一个数字并将其转化为无符号整数格式
- %x - 接受一个数字并将其转化为十六进制数格式, 使用小写字母
- %X - 接受一个数字并将其转化为十六进制数格式, 使用大写字母
- %e - 接受一个数字并将其转化为科学记数法格式, 使用小写字母e
- %E - 接受一个数字并将其转化为科学记数法格式, 使用大写字母E
- %f - 接受一个数字并将其转化为浮点数格式
- %g(%G) - 接受一个数字并将其转化为%e(%E, 对应%G)及%f中较短的一种格式
- %q - 接受一个字符串并将其转化为可安全被Lua编译器读入的格式
- %s - 接受一个字符串并按照给定的参数格式化该字符串
### 为进一步细化格式, 可以在%号后添加参数. 参数将以如下的顺序读入:

> - (1) 符号: 一个+号表示其后的数字转义符将让正数显示正号. 默认情况下只有负数显示符号.
- (2) 占位符: 一个0, 在后面指定了字串宽度时占位用. 不填时的默认占位符是空格.
- (3) 对齐标识: 在指定了字串宽度时, 默认为右对齐, 增加-号可以改为左对齐.
- (4) 宽度数值
- (5) 小数位数/字串裁切: 在宽度数值后增加的小数部分n, 若后接f(浮点数转义符, 如%6.3f)则设定该浮点数的小数只保留n位, 若后接s(字符串转义符, 如%5.3s)则设定该字符串只显示前n位.
     */
    export let format = _string.format as (this: void, str: string, ...arg: vararg) => any;
    export let byte = _string.byte as (this: void, str: string, ...arg: vararg) => any;
    export let len = _string.len as (this: void, str: string) => any;
    /**字符串反转 */
    export let reverse = _string.reverse as (this: void, str: string) => string;

    /**在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置多参数。不存在则返回 nil。 */
    export let find = _string.find as (this: void, str: string, substr: string, star?: number, end?: number) => any;
    /**mainString 为要操作的字符串， findString 为被替换的字符，replaceString 要替换的字符，num 替换次数（可以忽略，则全部替换） */
    export let gsub = _string.gsub as (this: void, mainString: string, findString: string, replaceString: string, num?: number) => any;
    /**字符串全部转为大写字母 */
    export let upper = _string.upper as (this: void, str: string) => any;
    /**字符串全部转为小写字母 */
    export let lower = _string.lower as (this: void, str: string) => any;
    /**回一个迭代器函数，每一次调用这个函数，返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到，迭代函数返回nil */
    export let gmatch = _string.gmatch as (this: void, str: string, pattern: string) => MyIterable;
    /**string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。 
    在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。
    返回多值
    > - .(点): 与任何字符配对
    - %a: 与任何字母配对
    - %c: 与任何控制符配对(例如\n)
    - %d: 与任何数字配对
    - %l: 与任何小写字母配对
    - %p: 与任何标点(punctuation)配对
    - %s: 与空白字符配对
    - %u: 与任何大写字母配对
    - %w: 与任何字母/数字配对
    - %x: 与任何十六进制数配对
    - %z: 与任何代表0的字符配对
    - %x(此处x是非字母非数字字符): 与字符x配对. 主要用来处理表达式中有功能的字符(^$()%.[]*+-?)的配对问题, 例如%%与%配对
    - [数个字符类]: 与任何[]中包含的字符类配对. 例如[%w_]与任何字母/数字, 或下划线符号(_)配对
    - [^数个字符类]: 与任何不包含在[]中的字符类配对. 例如[^%s]与任何非空白字符配对
    */
    export let match = _string.match as (this: void, str: string, pattern, init) => any;
    /**返回字符串string的n个拷贝 */
    export let rep = _string.rep as (this: void, str: string, n) => any;
    export let char = _string.char as (this: void, arg: any) => any;


    /** @luaIterator @tupleReturn */
    interface MyIterable extends Array<string[]> { };
};


function LOWORD(l) { return l & 0xffff }
function HIWORD(l) { return (l >> 16) & 0xffff }
function MAKELONG(a, b) { return (((b & 0xFFFF) << 16) | (a & 0xFFFF)) & 0xFFFFFFFF }
function LOBYTE(l) { return l & 0xff }
function HIBYTE(l) { return (l >> 8) & 0xff }
function MAKEWORD(a, b) { return (((b & 0xFF) << 8) | (a & 0xFF)) & 0xFFFF }


namespace event {
    let topics = {} as { [eventName: string]: { [handel: number]: Function } };
    let topicsCount = 0;
    /**发布事件 */
    export function publish(eventName, ...arg: vararg) {
        let q = topics[eventName];
        if (q) for (const key in q) q[key] && q[key](...arg);
    }
    /**订阅事件 返回索引 */
    export function subscribe(eventName: string, proc: Function) {
        let q = topics[eventName] = topics[eventName] || {};
        let handel = topicsCount++;
        q[handel] = proc;
        return handel;
    }
    /**删除订阅 一般用于销毁对象时调用。避免不必要的调用 
     * 第二个参数填写则删除指定索引。第二个参数不填写则删掉整个事件
     */
    export function removeSubscribe(eventName: string, handel?: number) {
        if (topics[eventName]) {
            if (handel && topics[eventName][handel]) {
                delete topics[eventName][handel];
            } else {
                delete topics[eventName];
            }
        }
    }
}

namespace os {
    /**获取系统启动到现在的毫秒数 */
    export function timeTick() {
        return ffi.C.GetTickCount();
    }
    /**暂停执行 请在cmd中使用 */
    export function pause() {
        os.execute("pause");
    }
}




class console {
    private static timeTicks = {};
    /**
     * 打印数据
     * @param arg 可打印多个 
     */
    static log(...arg: vararg) {
        return _G.print(...arg);
    }
    static dumpJson(...arg: vararg) {
        this._dump("Json", 3, ...arg);
    }
    static dump(...arg) {
        this._dump("Lua", 3, ...arg);
    }
    static _dump(tp: string, level = 3, ...arg) {
        let tab = debug.getinfo(level);
        let typeList = [];
        let s1 = { Lua: "=", Json: ":" };
        let s2 = { Lua: ",", Json: "," };
        arg.forEach(element => {
            typeList.push(`<${typeof (element)}>:${typeof (element) == "object" ? table.tostring(element,
                s1[tp], s2[tp]) : element}`);
        });
        this.log(`${tab.short_src}${tab.name ? ("[" + tab.name + "]") : ""}:${tab.currentline}`);
        this.log("---", "\n", typeList.join("\n\t"), "\n---");
    }

    static time(lable = "default") {
        if (!this.timeTicks[lable]) {
            this.timeTicks[lable] = os.timeTick();
            return true;
        }
    }
    static timeEnd(lable = "default") {
        if (this.timeTicks[lable]) {
            let endTime = os.timeTick();
            this.log(`${lable}:${endTime - this.timeTicks[lable]}`);
            this.timeTicks[lable] = null;
        }
    }
}

class math {

}
// /**启动时命令行 */
// export let _CMDLINE: string = (() => {
//     ffi.cdef("const char* GetCommandLineA()");
//     return ffi.string(ffi.call("GetCommandLineA"));
// })()

enum COLLECT_CONST {
    /**
     * 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能：
     */
    COLLECT = "collect",
    /**
     * 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分，所以只需要乘上 1024 就能得到 Lua 使用的准确字节数（除非溢出）。
     */
    COUNT = "count",
    /**
     * 重启垃圾收集器的自动运行。
     */
    RESTART = "restart",
    /**
     * 将 arg 设为收集器的 间歇率 （参见 §2.5）。 返回 间歇率 的前一个值。
     */
    SETPAUSE = "setpause",
    /**
     * 返回 步进倍率 的前一个值。
     */
    SETSTEPMUL = "setstepmul",
    /**
     * 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时，收集器步进（不可分割的）一步。 传入非 0 值， 收集器收集相当于 Lua 分配这些多（K 字节）内存的工作。 如果收集器结束一个循环将返回 true 。
     */
    STEP = "step",
    /**
     * 停止垃圾收集器的运行。 在调用重启前，收集器只会因显式的调用运行。
     */
    STOP = "stop",
}



enum OS_CONST {
    Windows = "Windows",
    Ios = "Ios",
    Linux = "Linux",
}

enum DUMP_CONST {
    Lua = "Lua",
    Json = "Json",
}